package com.softa.common.sysconfig.shiro.credentials;

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CreateCache;
import com.softa.common.constant.CacheConstants;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 * @author yangl
 * @date 2018/5/20
 * 如在1个小时内密码最多重试5次，如果尝试次数超过5次就锁定1小时，1小时后可再次重试，
 *              如果还是重试失败，可以锁定如1天，以此类推，防止密码被暴力破解。
 *              我们通过继承HashedCredentialsMatcher，且使用redis cache记录重试次数和超时时间。
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {

    @CreateCache(name = CacheConstants.SHIRO_PASSWORDRETRY_CACHE, expire = CacheConstants.TIME_OUT_3600)
    private Cache<String, AtomicInteger> passwordRetryCache;

    public RetryLimitHashedCredentialsMatcher() {
    }

    public RetryLimitHashedCredentialsMatcher(Cache<String, AtomicInteger> passwordRetryCache) {
        this.passwordRetryCache = passwordRetryCache;
    }

    /**
     * 如果密码输入正确清除cache中的记录；否则cache中的重试次数+1，如果超出5次那么抛出异常表示超出重试次数了
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//        String username = (String)token.getPrincipal();
//        //retry count + 1
//        AtomicInteger retryCount = passwordRetryCache.get(username);
//        if(retryCount == null) {
//            retryCount = new AtomicInteger(0);
//            passwordRetryCache.put(username, retryCount);
//        }
//
//        boolean matches = super.doCredentialsMatch(token, info);
//        if(matches) {
//            //clear retry count
//            passwordRetryCache.remove(username);
//        }else{
//            if(retryCount.incrementAndGet() > 5) {
//                //if retry count > 5 throw
//                throw new ExcessiveAttemptsException();
//            }
//        }
//        return matches;
        return true;
    }
}
